「依程式碼自動產生 API 文件」是 Django Ninja 的一大賣點。
事實上,API 文件的自動化,正是我在工作上的專案從 Django REST framework 轉向 Django Ninja 的首要考量——也是我開始學習 Django Ninja 的契機。
Django Ninja 省去了大量人工撰寫(我們用 API Blueprint)API 文件的時間,特別是在 API 規格變動時,不需要再同步修改文件,大大減少了維護文件的心力。
可見這個特性有多麼重要。
那麼,為何我到了系列的第 17 篇文章——也就是本篇,才開始介紹 Django Ninja 的 API 文件功能呢?
原因在於,要產生優秀的 API 文件,需要你對 Schema 的使用有一定的了解。所以我不得不放在第三章之後。
現在,我們要開始探討如何使用 Django Ninja 產出高品質的 API 文件。
本文所有的程式碼變動,可參考這個 PR。
現代軟體開發中,「Documentation as Code」(DoC) 是一個逐漸被認可的理念。
DoC 是指將文件與程式碼緊密結合,開發人員使用「與軟體程式碼開發相同的流程和工具」來建立和維護文件。
文件隨著程式碼的變更而自動更新,兩者無時無刻都保持著一致性。
這不僅提升了開發效率,還減少了因文件過時而導致的溝通不順暢或誤解。
Django Ninja 的自動化產生文件功能無疑是「Documentation as Code」精神的實踐。
我們可以透過撰寫 API 路由、view 函式、Schema 等程式碼,自動產生符合 OpenAPI 標準的文件,當程式碼變更時,這些文件也會自動反映變更結果,無需手動維護。
在 Django Ninja 中,要產生高品質的 API 文件,主要涉及兩個重點:
看到這裡,是不是有點小期待呢?☺️
在開始大刀闊斧地加強文件品質之前,我們先來看看目前 API 文件有多麼「陽春」。
啟動 Django 伺服器後,造訪下列網址可以查看當前 API 的文件:
http://127.0.0.1:8000/docs
目前的文件內容如下:
我們從「對開發人員是否有用、好讀」的角度進行簡要分析。
首先,它沒有分組!
Django user app 和 post app 的 API 都混在一起了,當 API 愈來愈多,就會顯得十分雜亂。這是最優先要解決的問題。
其次,API 的說明如「Get Users」、「Create Post」等,顯然都是從 view 函式名稱自動轉換而來。資訊有限,且不夠口語、不夠詳細,簡單講就是不夠「讀者友善」。
我們點進唯一的 POST API 看一下內容:
內頁的說明有「新增文章」,這其實是從 view 函式的 docstring 獲取的:
@router.post(path='/posts/')
def create_post(...) -> tuple[int, dict]:
"""
新增文章
"""
...
再看一下 HTTP 請求 body 的範例:
{
"title": "string",
"content": "string",
"user_id": 0
}
只能說不盡理想,像"string"
這樣的例子,只有表現出「型別」,沒有模擬真實世界的文章標題或內容。
上述問題正是我們需要改善 API 文件的重點。
其中,請求 body 範例與 Schema 設定有關,是下一篇的重心。
本文先聚焦於 Django Ninja 設定——讓我們一一介紹。
我們第一個要解決的是 API 的分組(分類)問題。
為了讓文件結構更清晰,Django Ninja 支援使用 Tags 來對 API 進行分組。不僅有助於組織文件,還能讓開發者或使用者更快地找到所需的 API。
Tags 分組可以在兩個地方進行設定。
最常見的做法,是在一級路由進行設定:
# NinjaForum/арі.py
api.add_router(prefix='', router='user.api.router', tags=['User'])
api.add_router(prefix='', router='post.api.router', tags=['Post'])
因為同一個 Django app 的 API,通常就是在同一組。
也可以在路由裝飾器設定分組,但我認為這屬於相對「例外」的情況,主要用於:
比如下面範例中,沒有區分 Django app,但仍有分組需求:
from ninja import NinjaAPI
api = NinjaAPI()
@api.get("/users/", tags=["User"])
def get_users(request):
...
@api.post("/posts/", tags=["Post"])
def create_post(request, title: str):
...
大部分情況,我推薦在一級路由進行分組就好。
不然像上面的例子那樣,一個一個標記,實踐起來有點繁瑣。
Django Ninja 的路由裝飾器不僅可以設定基本的 API 路徑,還允許你加上 API 的描述文字,這些內容會直接反映在生成的 API 文件中。
透過這些設定,你可以為 API 的參數、回應、甚至意圖加上說明,讓文件更全面。
透過description
和summary
參數,能為各 API 路由提供說明。不過description
會取代上述「docstring 轉 API 說明」的效果,所以我平常都只寫summary
。
畢竟我們寫 Python,docstring 可是必須的!
程式碼如下:
@router.get(..., summary='取得文章列表')
def get_posts(...) -> QuerySet[Post]:
"""
取得文章列表
"""
...
到目前為止的實際改善效果:(分組、API 說明)
非常不錯唷!
你可以通過NinjaAPI
類別的初始化設定,來客製一些全域的 API 文件細節。比如:
# NinjaForum/арі.py
from ninja import NinjaAPI
api = NinjaAPI(
title="忍者論壇 API",
version="1.0",
description="這是忍者論壇的 API 文件,供讀者參考"
)
對應的實際效果:
NinjaAPI
的初始化設定非常多樣——有些可能是你需要的。
這裡只是簡單例示,更多設定細節,可查看文件。
本文探討了 Django Ninja 自動生成 API 文件的常見設定,讓「Documentation as Code」精神得以實現——程式碼與文件能輕鬆保持一致,減少了手動維護的麻煩。
然而,API 文件的品質,還取決於我們如何定義 Schema 中的細節。
接下來,我們將深入探討這個議題,說明如何透過 Pydantic 的Field
參數設定,提供高品質的文件範例,進一步提升 API 文件的可讀性和清晰度。
本文同步發表於我的部落格——Code and Me
認同 Documentation as Code 的作法!
code 寫好,文件也同時寫好了,不用額外花時間寫文件,更不用擔心會有文件與實際不一致的問題。
至少 FastAPI 如果有問題會在內部發生錯誤,不會傳一個跟文件規格不一樣的結果,而這問題通常應該在測試時候你就知道你 API 跑不過了。
目前看起來 Ninja 真的跟 FastAPI 有87%像,學過FastAPI的我再來學 Ninja 真的一路看下來暢行無阻,有困難的點的通常都是在比較 DRF 原生跟 Ninja 的差異時候XDDD
其實會想學 Django 也是因為知道工作的大環境還是以 Django 較多,不少工作需求都直接說要有 Django N年經驗之類的。那如果只學過 Ninja 可以自稱學過 Django 嗎? 哈哈哈
有困難的點的通常都是在比較 DRF 原生跟 Ninja 的差異時候XDDD
如果不寫 DRF,就不需要比較了,哈哈哈哈哈
其實會想學 Django 也是因為知道工作的大環境還是以 Django 較多,不少工作需求都直接說要有 Django N年經驗之類的
真的假的?驚!我以為是 Django、Flask、FastAPI 三分天下說,難道是我太久沒求職與市場脫節😂
有些比較有流量的公司,都是搞微服務,API 被輕量化,所以不用 Django,而是 Flask 居多(比如中國的豆瓣)
但有了 FastAPI 以後,Flask 地位可能會受到一定程度的威脅,畢竟兩者在屬性上比較類似,但 FastAPI 有 type hints 和自動化文件!